package com.chen.base.module.base.service.impl;

import com.chen.base.module.base.dto.PageResponse;
import com.chen.base.module.base.dto.Response;
import com.chen.base.module.base.service.BaseService;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.CrudRepository;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.lang.reflect.Field;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <b>一句话描述该类</b>
 * <p>
 * 描述:<br>
 * 
 * @author 威 
 * <br>2019年10月26日 下午6:04:52 
 * @see
 * @since 1.0
 */
public abstract class BaseServiceImpl<T> implements BaseService<T> {
	private Logger log = LoggerFactory.getLogger(BaseServiceImpl.class);
	
	@Autowired
	@Qualifier("test2EntityManager")
	private EntityManager test2EntityManager;

	@Autowired
	@Qualifier("entityManager")
	private EntityManager entityManager;

	/**
	 * @Author chenjiwei
	 * @Description sql语句查询
	 * @Date 2020/10/31 1:29
	 * @Param sql sql语句， resultClass 返回类型， params 查询条件，按下标
	 * @return
	 */
	protected <T> Query nativeQuery(String sql, Class<T> resultClass, List<Object> params) {
		Query query = null;
		if (resultClass == null) {
			query = test2EntityManager.createNativeQuery(sql);
		} else {
			query = test2EntityManager.createNativeQuery(sql, resultClass);
		}
		log.info("query sql = {}", sql);
		if (params != null && params.size() > 0) {
			log.debug("params = {}", params);
			for (int i = 0; i < params.size(); i++) {
				query.setParameter(i + 1, params.get(i));
			}
		}
		return query;
	}

	protected <T> Query nativeQueryOfTest(String sql, Class<T> resultClass, Map<String, Object> params) {
		Query query = null;
		if (resultClass == null) {
			query = entityManager.createNativeQuery(sql);
		} else {
			query = entityManager.createNativeQuery(sql, resultClass);
		}
		log.info("query sql = {}", sql);
		params.forEach(query::setParameter);
		return query;
	}

	/**
	 * @Author chenjiwei
	 * @Description 根据sql语句查询
	 * @Date 2020/10/31 1:28
	 * @Param sql sql语句，params 查询条件，对应下标
	 * @return
	 */
	public List<Map<String, Object>> nativeQueryForList(String sql, List<Object> params){
		Query query = nativeQuery(sql, null, params);
		return query.unwrap(NativeQueryImpl.class)
				.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP)
				.getResultList();
		// 可以理解为使用setResultTransformer的场景是：只有个别几个字段需要映射到实体类上（非全部字段一一对应）

	}

	public Response nativeQueryForMap(String sql, Class<?> clazz, Map<String, Object> params){
		Query query = nativeQueryOfTest(sql, null, params);
		query.unwrap(NativeQueryImpl.class)
				.setResultTransformer(Transformers.aliasToBean(clazz));
		return Response.success("成功", query.getResultList());
	}

	public PageResponse nativeQueryForMap(String sql, String countSql, Class<?> clazz, Map<String, Object> params){
		Query query = nativeQueryOfTest(sql, null, params);
		query.unwrap(NativeQueryImpl.class)
				.setResultTransformer(Transformers.aliasToBean(clazz));

		Query countQuery = nativeQueryOfTest(countSql, null, params);
		countQuery.unwrap(NativeQueryImpl.class);

		List list = query.getResultList();

		return PageResponse.success("查询成功", list, Integer.parseInt(countQuery.getSingleResult().toString()));
	}

	
	/**
	 * @Author chenjiwei
	 * @Description 
	 * @Date 2020/10/31 1:27
	 * @Param t 查询的条件实体类， arg1 分页对象
	 * @return 
	 */
	public Page<T> findAll(T t, Pageable arg1) {
		Page<T> pages = (Page<T>) getBaseRepository().findAll(
		new Specification<T>(){
			@Override
			public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				List<Predicate> ps = execute(filterForFindAll(),
						root, t, cb);
				return query.where(ps.toArray(new Predicate[ps.size()])).getRestriction();
			}
		}, arg1);
		return pages;
	}
	
	@Override
	public abstract JpaSpecificationExecutor<T> getBaseRepository();
 
	@SuppressWarnings("unchecked")
	public void delete(Iterable<? extends T> entities){
		((CrudRepository<T, String>) getBaseRepository()).deleteAll(entities);
	}
	
	/**
	 * 根据属性设定条件格式
	 * <p>	 
	 * @return
	 * String<br/>
	 * 		例如：code-like,zhcn-like<br/>
	 * 			code属性将进行模糊查询<br/>
	 * 			zhcn也进行模糊插叙<br/>
	 * 		其他查询方式还有：<br/>
	 * 			equal<br/>
	 * 		    like <br/>
	 * @see
	 * @since 1.0
	 */
	protected abstract String filterForFindAll();

	/**
	 * findAll 中执行此方法
	 * <p>	 
	 * @param filter
	 * @param root
	 * @param t
	 * @param cb
	 * @return
	 * List<Predicate>
	 * @see #filterForFindAll()
	 * @see #findAll(Object, Pageable)
	 * @since 1.0
	 */
	protected List<Predicate> execute(String filter, Root<T> root, T t, CriteriaBuilder cb){
		List<Predicate> ps = new ArrayList<>();
		if(t == null) return ps;
		Map<String, Object> param = parseMapByEntity(t);
		if(param.size() == 0) return ps;
		Map<String, String> token = getToken(filter);
		Iterator<Entry<String, Object>> it = param.entrySet().iterator();
		while (it.hasNext()){
			Entry<String, Object> item = it.next();
			ps.add(coverPredicate(root, token.get(item.getKey()),  item, cb));
		}
		return ps;
	}
	
	/**
	 * 将此类型的字符串name-like,age-equal转换成map
	 * <p>	 
	 * @param str
	 * @return
	 * Map<String,String>
	 * @see #execute(String, Root, Object, CriteriaBuilder)
	 * @since 1.0
	 */
	protected Map<String, String> getToken(String str){
		Map<String, String> map = new HashMap<>();
		if(str == null || "".equals(str)) return map;
		String[] arr = str.split(",");
		Stream.of(arr).collect(Collectors.toMap(
				item -> ((String) item).substring(0, ((String) item).indexOf("_")),
				item -> ((String) item).substring(((String) item).indexOf("_")+1)
				));
//		for(String s : arr){
//			String[] arr_ = s.split("-");
//			map.put(arr_[0], arr_[1]);
//		}
		return map;
	}
	
	/**
	 * 生成Predicate对象
	 * <p>	 
	 * @param root
	 * @param token
	 * @param item
	 * @param cb
	 * @return
	 * Predicate
	 * @see
	 * @since 1.0
	 */
	protected Predicate coverPredicate(Root<T> root, String token, Entry<String, Object> item, CriteriaBuilder cb){
		if(token == null || "".equals(token)) token = "equal";
		switch(token){
			case "like":
				return cb.like(root.<String> get(item.getKey()), "%"+item.getValue()+"%");
			case "equal":
				return cb.equal(root.<String> get(item.getKey()), item.getValue());
		}
		return null;
	}
	
	/**
	 * 只获取private修饰的字段
	 * <p>	 
	 * 获取字段值非空的 键值 <br>
	 * 在修改和插入的时候会调用到
	 * @param target
	 * @return
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * Map<String,Object>
	 * @see
	 * @since 1.0
	 */
	protected Map<String, Object> parseMapByEntity(Object target){
		Class<?> clazz = target.getClass();// 获取PrivateClass整个类
//		Object pc = clazz.newInstance();// 创建一个实例
		Field[] fs = clazz.getDeclaredFields();// 获得某个类的所有声明的字段，即包括public、private和proteced，但是不包括父类的申明字段。
		Field field;Object value;
		Map<String, Object> map = new HashMap<>();
		try {
			for (int i = 0; i < fs.length; i++) {
				(field = fs[i]).setAccessible(true);// 将目标属性设置为可以访问
				
					if((value = field.get(target)) != null 
							&& field.getModifiers() == 2) // PRIVATE: 2
						map.put(field.getName(), value);
				
			} 
		} catch (IllegalArgumentException | IllegalAccessException e) {
			e.printStackTrace();
		}
		return map;
	}

	@Override
	public void getById(String id) {
		((CrudRepository<T, String>) getBaseRepository()).findById(id);
	}
}


